
import struct
from ModbusRTU import CModbus


class CDCMP:

    FUNC_ENABLE_DCMP = 0X01
    FUNC_DEV_INFO = 0X02
    FUNC_SET_KEYPAD_MODE = 0X03

    DI_PRODUCT_ID = 0
    DI_BUFFER_LEN = 1
    DI_FIRMWARE_VERSION = 2
    DI_DCMP_VERSION = 3
    DI_KEYPAD_PROTOCOL = 4
    DI_COUNT = 5


    DT_8BIT = 0
    DT_16BIT = 1
    DT_32BIT = 2

    PID_SERIES_POS		= 26
    PID_SERIES_MASK	=	0XFC000000
    PID_TYPE_POS		= 21
    PID_TYPE_MASK		= 0X03E00000
    PID_SUB_TYPE_POS	= 16
    PID_SUB_TYPE_MASK	= 0X001F0000
    PID_DERIVATIVE_POS	= 8
    PID_DERIVATIVE_MASK	= 0X0000FF00
    PID_BOOTLOADER_MASK= 0X00000080


    PID_SERIES_LIST = [\
        'PID_SERIES_GENERIC', \
        'PID_SERIES_FD0', \
        'PID_SERIES_EVD',\
        'PID_SERIES_FD6']

    PID_TYPE_LIST = [\
        'PID_TYPE_GENERIC', \
        'PID_TYPE_DRIVE',\
        'PID_TYPE_KEYPAD',\
        'PID_TYPE_ENCODER_OPTION',\
        'PID_TYPE_IO_OPTION',\
        'PID_TYPE_PROFIBUS_OPTION']


    INFO_SIZE_TABLE = [2, 2, 4, 2, 2]


    def __init__(self, outerProtocol):
        self.outerProtocol = outerProtocol
        self.deviceBufferSize = 32


    def EnableDCMP(self):

        data = bytearray()
        data.append(0x01)
        data.append(0x0E)
        data.append(0x6E)
        data.append(0x0A)
        data.append(0x0B)
        data.append(0x6C)
        data.append(0x0E)

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        if err != 0:
            value = 0
        else:
            value = data[1]

        return err, value

    def ParseProductID(self, id):
        series = (id & CDCMP.PID_SERIES_MASK) >> CDCMP.PID_SERIES_POS
        type = (id & CDCMP.PID_TYPE_MASK) >> CDCMP.PID_TYPE_POS
        bootloader = id & CDCMP.PID_BOOTLOADER_MASK

        if bootloader == 0:
            programType = 'Application'
        else:
            programType = 'Bootloader'

        info = {'series' : CDCMP.PID_SERIES_LIST[series], 'type': CDCMP.PID_TYPE_LIST[type], 'programType': programType}

        return info



    def RequestDeviceInfo(self, infoList):

        data = bytearray()
        data.append(0x02)

        for info in infoList:
            data.append(info)

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        pointer = 0

        info = {}

        if err == 0 and data[0] == self.FUNC_DEV_INFO:

            pointer = pointer + 1

            while pointer < len(data) - 1:

                if data[pointer] == CDCMP.DI_PRODUCT_ID:
                    pointer = pointer + 1
                    # big endian, unsigned short
                    pid = struct.unpack('>I', data[pointer:pointer + 4])[0]
                    pInfo = self.ParseProductID(pid)
                    info.update(pInfo)

                    pointer = pointer + 4

                elif data[pointer] == CDCMP.DI_BUFFER_LEN:

                    pointer = pointer + 1
                    info['bufferLen'] = struct.unpack('>H', data[pointer:pointer + 2])[0]
                    pointer = pointer + 2
                    self.deviceBufferSize = (info['bufferLen'] & 0xFFFE) - 10

                elif data[pointer] == CDCMP.DI_DCMP_VERSION:

                    pointer = pointer + 1
                    info['dcmpVersion'] = '%04d'%struct.unpack('>H', data[pointer:pointer + 2])[0]
                    pointer = pointer + 2

                elif data[pointer] == CDCMP.DI_KEYPAD_PROTOCOL:
                    pointer = pointer + 1
                    info['keypadVersion'] = '%04d'%struct.unpack('>H', data[pointer:pointer + 2])[0]
                    pointer = pointer + 2

                elif data[pointer] == CDCMP.DI_FIRMWARE_VERSION:
                    pointer = pointer + 1
                    info['firmwareVersion'] = '%08d'%struct.unpack('>I', data[pointer:pointer + 4])[0]
                    pointer = pointer + 4


        return err, info




    def SetKeypadMode(self, mode):

        data = bytearray()
        data.append(0x03)
        data.append(mode)

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        if err != 0:
            value = None
        else:
            value = struct.unpack('>b', data[1:2])[0]

        return err, value



    def ReadMemory(self, size, type, count, startAddress):

        data = bytearray()
        data.append(0x04)

        property = (size << 14) + count

        data.append((property >> 8) & 0xFF)
        data.append(property & 0xFF)
        data.append((startAddress >> 24) & 0xFF)
        data.append((startAddress >> 16) & 0xFF)
        data.append((startAddress >> 8) & 0xFF)
        data.append(startAddress & 0xFF)

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        value = []

        if err == 0:

            #offset function code
            pointer = 1

            property = struct.unpack('>H', data[pointer:pointer + 2])[0]
            size = property >> 14
            pointer = pointer + 2

            while pointer < len(data):

                if type == 'Float':
                    value.append(struct.unpack('>f', data[pointer:pointer+4])[0])
                    pointer = pointer + 4                    

                elif size == self.DT_8BIT:
                    value.append(data[pointer])
                    pointer = pointer + 1

                elif size == self.DT_16BIT:
                    if type == 'Int':
                        fmt = '>h'
                    else:
                        fmt = '>H'
                    value.append(struct.unpack(fmt, data[pointer:pointer + 2])[0])
                    pointer = pointer + 2

                else:
                    if type == 'Int':
                        fmt = '>i'
                    else:
                        fmt = '>I'
                    value.append(struct.unpack(fmt, data[pointer:pointer + 4])[0])
                    pointer = pointer + 4


        return err, value


    def WriteMemory(self, size, type,  startAddress, valueList):

        data = bytearray()
        data.append(0x05)

        property = (size << 14) + len(valueList)

        data.append((property >> 8) & 0xFF)
        data.append(property & 0xFF)
        data.append((startAddress >> 24) & 0xFF)
        data.append((startAddress >> 16) & 0xFF)
        data.append((startAddress >> 8) & 0xFF)
        data.append(startAddress & 0xFF)

        for value in valueList:
            
            if type == 'Float':
                bytes = struct.pack('>f', value)
                data.append(bytes)

            elif size == self.DT_8BIT:
                data.append(value & 0xFF)

            elif size == self.DT_16BIT:
                data.append((value >> 8) & 0xFF)
                data.append(value & 0xFF)

            else:
                data.append((value >> 24) & 0xFF)
                data.append((value >> 16) & 0xFF)
                data.append((value >> 8) & 0xFF)
                data.append(value & 0xFF)

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        count = 0

        if err == 0:
            count = (struct.unpack('>H', data[1:3])[0]) & 0x3FFF;

        return err, count


    def Reset(self, resetType):

        data = bytearray()
        data.append(0x06)
        data.append(resetType)

        # Reset request may have no response
        try:
            self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)
        except:
            pass

#        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)
#
#
#        if err != 0:
#            value = None
#        else:
#            value = struct.unpack('>b', data[1:2])[0]
#
#        return err, value


    def OpenFile(self, mode, endian, fileName):

        data = bytearray()
        data.append(0x20)
        data.append(ord(mode))
        data.append(ord(endian))

        for ch in fileName:
            data.append(ord(ch))

        data.append(ord('\0'))

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        handle = 0

        if err == 0:
            handle = struct.unpack('>h', data[1:3])[0]

        return err, handle


    def WriteFile(self, handle, fileData):

        data = bytearray()
        data.append(0x21)
        data.append(handle >> 8)
        data.append(handle & 0xFF)

        data = data + fileData

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        status = 0

        if err == 0:
            status = struct.unpack('>h', data[3:5])[0]

        return err, status


    def ReadFile(self, handle, count):

        data = bytearray()
        data.append(0x22)
        data.append(handle >> 8)
        data.append(handle & 0xFF)
        data.append(count >> 8)
        data.append(count & 0xFF)

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        if err == 0:

            status = struct.unpack('>h', data[3:5])[0]

            if status > 0:
                return err, status, data[5:]
            else:
                return err, status, []

        return err, None, []

    def CloseFile(self, handle, len, crc32):

        data = bytearray()
        data.append(0x23)
        data.append(handle >> 8)
        data.append(handle & 0xFF)
        data.append((len >> 24) & 0xFF)
        data.append((len >> 16) & 0xFF)
        data.append((len >> 8) & 0xFF)
        data.append(len & 0xFF)
        data.append((crc32 >> 24) & 0xFF)
        data.append((crc32 >> 16) & 0xFF)
        data.append((crc32 >> 8) & 0xFF)
        data.append(crc32 & 0xFF)

        err, data = self.outerProtocol.ProtocolWrapper(CModbus.FC_DCMP, data)

        status = struct.unpack('>h', data[3:5])[0]

        return err, status
